﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Runtime.Serialization
Imports System.Runtime.InteropServices
Imports System.Collections.Specialized
Imports System.Windows.Data
Imports System.Linq

'Public Class ObservableDictionaryV2(Of TKey, TValue)
'    Implements IDictionary(Of TKey, TValue)

'    Protected WithEvents _obskeyValuePairs As New ObservableCollection(Of KeyValuePair(Of TKey, TValue))
'    Protected _trueDictionary As New Dictionary(Of TKey, TValue)

'    'Protected _keyedEntryCollection As System.Collections.Specialized

'    'Public Sub Add(ByVal item As KeyValuePair(Of TKey, TValue))
'    '   _obsKeyValuePairCollection.Add(item)
'    'Ajout de la clé dans la liste des clés
'    'End Sub

'    Public Sub Add(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TValue)) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Add
'        _trueDictionary.Add(item.Key, item.Value)
'        _obskeyValuePairs.Add(item)
'        '_obskeyValuePairs.
'    End Sub
'    Public Sub Clear() Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Clear
'        _keyValuePairs.Clear()
'        _keys.Clear()
'        _values.Clear()
'    End Sub
'    Public Function Contains(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TValue)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Contains
'        Return _keyValuePairs.Contains(item)
'    End Function
'    Public Sub CopyTo(ByVal array() As System.Collections.Generic.KeyValuePair(Of TKey, TValue), ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).CopyTo
'        _keyValuePairs.CopyTo(array, arrayIndex)
'    End Sub
'    Public ReadOnly Property Count As Integer Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Count
'        Get
'            Return _keyValuePairs.Count
'        End Get
'    End Property
'    Public ReadOnly Property IsReadOnly As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).IsReadOnly
'        Get
'            Return False
'        End Get
'    End Property
'    Public Function Remove(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TValue)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Remove
'        If _keyValuePairs.Remove(item) Then
'            _keys.Remove(item.Key)
'            _values.Remove(item.Value)
'            Return True
'        Else
'            Return False
'        End If
'    End Function

'    Public Sub Add(ByVal key As TKey, ByVal value As TValue) Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Add
'        Add(New KeyValuePair(Of TKey, TValue)(key, value))
'    End Sub
'    Public Function ContainsKey(ByVal key As TKey) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TValue).ContainsKey
'        Return _keys.Contains(key)
'    End Function
'    Default Public Property Item(ByVal key As TKey) As TValue Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Item
'        Get
'            Return _trueDictionary.Item(key)
'        End Get
'        Set(ByVal value As TValue)
'            _trueDictionary.Item(key) = value
'        End Set
'    End Property
'    Public ReadOnly Property Keys As System.Collections.Generic.ICollection(Of TKey) Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Keys
'        Get
'            Return _keys
'        End Get
'    End Property

'    Public Function Remove(ByVal key As TKey) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Remove
'        'Récupérerle keyValuePairItem correspondant à la clé
'        Return Nothing
'    End Function

'    Public Function TryGetValue(ByVal key As TKey, ByRef value As TValue) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TValue).TryGetValue
'        'A implémenter
'        Return Nothing
'    End Function

'    Public ReadOnly Property Values As System.Collections.Generic.ICollection(Of TValue) Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Values
'        Get
'            'A implémenter
'            Return Nothing
'        End Get
'    End Property

'    Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)) Implements System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).GetEnumerator
'        'A implémenter
'        Return Nothing
'    End Function

'    Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
'        'A implémenter
'        Return Nothing
'    End Function



'    '#Region "ICollection"
'    '    Public Sub ICollection_Add(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TValue)) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Add

'    '    End Sub
'    '    Public Sub ICollection_Clear() Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Clear

'    '    End Sub
'    '    Public Function ICollection_Contains(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TValue)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Contains

'    '    End Function
'    '    Public Sub ICollection_CopyTo(ByVal array() As System.Collections.Generic.KeyValuePair(Of TKey, TValue), ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).CopyTo

'    '    End Sub
'    '    Public ReadOnly Property ICollection_Count As Integer Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Count
'    '        Get

'    '        End Get
'    '    End Property
'    '    Public ReadOnly Property ICollection_IsReadOnly As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).IsReadOnly
'    '        Get

'    '        End Get
'    '    End Property
'    '    Public Function ICollection_Remove(ByVal item As System.Collections.Generic.KeyValuePair(Of TKey, TValue)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).Remove

'    '    End Function
'    '    Public Function ICollection_GetEnumerator() As System.Collections.Generic.IEnumerator(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)) Implements System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of TKey, TValue)).GetEnumerator

'    '    End Function
'    '    Public Function ICollection_GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator

'    '    End Function
'    '#End Region

'End Class

Public Class ObservableDictionaryV3(Of TKey, TValue)
    Inherits ObservableCollection(Of KeyValuePair(Of TKey, TValue))
    Implements IDictionary(Of TKey, TValue)

    Public Overloads Sub Add(ByVal key As TKey, ByVal value As TValue) Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Add
        Add(New KeyValuePair(Of TKey, TValue)(key, value))
    End Sub
    Public Function ContainsKey(ByVal cle As TKey) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TValue).ContainsKey
        Dim keyList = From k In Me.Items
                      Where k.Key.Equals(cle)
                      Select k.Key
        If keyList.AsEnumerable() Is Nothing Then Return False Else Return True
    End Function

    Default Public Overloads Property Item(ByVal key As TKey) As TValue Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Item
        Get
            Dim result = From k In Me.Items
                         Where k.Key.Equals(key)
                         Select k.Value
            Return DirectCast(result.AsEnumerable(0), TValue)
        End Get
        Set(ByVal value As TValue)
            Dim result = From k In Me.Items
                         Where k.Key.Equals(key)
                         Select k
            Dim itChanged As KeyValuePair(Of TKey, TValue) = result.AsEnumerable(0)
            'it = New KeyValuePair(Of TKey, TValue)(it.Key, value)
        End Set
    End Property

    Public ReadOnly Property Keys As System.Collections.Generic.ICollection(Of TKey) Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Keys
        Get
            Dim result = From k In Me.Items
                         Select k.Key
            Return result.AsEnumerable()
        End Get
    End Property

    Public Overloads Function Remove(ByVal key As TKey) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Remove
        MsgBox("Remove Non implémentée")
        Return False
    End Function

    Public Function TryGetValue(ByVal key As TKey, ByRef value As TValue) As Boolean Implements System.Collections.Generic.IDictionary(Of TKey, TValue).TryGetValue
        Return False
    End Function

    Public ReadOnly Property Values As System.Collections.Generic.ICollection(Of TValue) Implements System.Collections.Generic.IDictionary(Of TKey, TValue).Values
        Get
            Dim result = From k In Me.Items
                         Select k.Value
            Return result
        End Get
    End Property

End Class

<Serializable()> _
Public Class ObservableDictionary(Of TKey, TValue)
    Implements IDictionary(Of TKey, TValue), ICollection(Of KeyValuePair(Of TKey, TValue)),  _
               IEnumerable(Of KeyValuePair(Of TKey, TValue)), IDictionary, ICollection, IEnumerable, ISerializable,  _
               IDeserializationCallback, INotifyCollectionChanged, INotifyPropertyChanged


#Region "fields"

    Protected _keyedEntryCollection As New KeyedDictionaryEntryCollection(Of TKey)

    Private _countCache As Integer = 0
    Private _dictionaryCache As New Dictionary(Of TKey, TValue)()
    Private _dictionaryCacheVersion As Integer = 0
    Private _version As Integer = 0

    <NonSerialized()> _
    Private _siInfo As SerializationInfo = Nothing

#End Region

#Region "constructors"

#Region "public"

    Public Sub New()
        _keyedEntryCollection = New KeyedDictionaryEntryCollection(Of TKey)()
    End Sub

    Public Sub New(ByVal dictionary As IDictionary(Of TKey, TValue))
        _keyedEntryCollection = New KeyedDictionaryEntryCollection(Of TKey)()

        For Each entry As KeyValuePair(Of TKey, TValue) In dictionary
            DoAddEntry(DirectCast(entry.Key, TKey), DirectCast(entry.Value, TValue))
        Next
    End Sub

    Public Sub New(ByVal comparer As IEqualityComparer(Of TKey))
        _keyedEntryCollection = New KeyedDictionaryEntryCollection(Of TKey)(comparer)
    End Sub

    Public Sub New(ByVal dictionary As IDictionary(Of TKey, TValue), ByVal comparer As IEqualityComparer(Of TKey))
        _keyedEntryCollection = New KeyedDictionaryEntryCollection(Of TKey)(comparer)

        For Each entry As KeyValuePair(Of TKey, TValue) In dictionary
            DoAddEntry(DirectCast(entry.Key, TKey), DirectCast(entry.Value, TValue))
        Next
    End Sub

#End Region

#Region "protected"

    Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        _siInfo = info
    End Sub

#End Region

#End Region

#Region "properties"

#Region "public"

    Public ReadOnly Property Comparer() As IEqualityComparer(Of TKey)
        Get
            Return _keyedEntryCollection.Comparer
        End Get
    End Property

    Public ReadOnly Property Count() As Integer
        Get
            Return _keyedEntryCollection.Count
        End Get
    End Property

    Public ReadOnly Property Keys() As Dictionary(Of TKey, TValue).KeyCollection
        Get
            Return TrueDictionary.Keys
        End Get
    End Property

    Default Public Property Item(ByVal key As TKey) As TValue
        Get
            Return DirectCast(_keyedEntryCollection(key).Value, TValue)
        End Get
        Set(ByVal value As TValue)
            DoSetEntry(key, value)
            OnPropertyChanged(Binding.IndexerName)
        End Set
    End Property

    Public ReadOnly Property Values() As Dictionary(Of TKey, TValue).ValueCollection
        Get
            Return TrueDictionary.Values
        End Get
    End Property

#End Region

#Region "private"

    Private ReadOnly Property TrueDictionary() As Dictionary(Of TKey, TValue)
        Get
            If _dictionaryCacheVersion <> _version Then
                _dictionaryCache.Clear()
                For Each entry As DictionaryEntry In _keyedEntryCollection
                    _dictionaryCache.Add(DirectCast(entry.Key, TKey), DirectCast(entry.Value, TValue))
                Next
                _dictionaryCacheVersion = _version
            End If
            Return _dictionaryCache
        End Get
    End Property

#End Region

#End Region

#Region "methods"

#Region "public"

    Public Sub Add(ByVal key As TKey, ByVal value As TValue)
        DoAddEntry(key, value)
    End Sub

    Public Sub Clear()
        DoClearEntries()
    End Sub

    Public Function ContainsKey(ByVal key As TKey) As Boolean
        Return _keyedEntryCollection.Contains(key)
    End Function

    Public Function ContainsValue(ByVal value As TValue) As Boolean
        Return TrueDictionary.ContainsValue(value)
    End Function

    Public Function GetEnumerator() As IEnumerator
        Return New Enumerator(Of TKey, TValue)(Me, False)
    End Function

    Public Function Remove(ByVal key As TKey) As Boolean
        Return DoRemoveEntry(key)
    End Function

    Public Function TryGetValue(ByVal key As TKey, ByRef value As TValue) As Boolean
        Dim result As Boolean = _keyedEntryCollection.Contains(key)
        value = IIf(result, DirectCast(_keyedEntryCollection(key).Value, TValue), Nothing)
        Return result
    End Function

    Public Sub NotifyCollectionChange(ByVal DescChamps As Object, ByVal Champs As Object)
        'OnPropertyChanged("Count")
        'OnPropertyChanged("Item[]")
        'OnPropertyChanged("Keys")
        'OnPropertyChanged("Values")
        OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
        Dim oldItem As New KeyValuePair(Of TKey, TValue)(DirectCast(DescChamps, TKey), DirectCast(Champs, TValue))
        Dim newItem As New KeyValuePair(Of TKey, TValue)(DirectCast(DescChamps, TKey), DirectCast(Champs, TValue))
        OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, oldItem, newItem))
        'OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, New KeyValuePair(Of TKey, TValue)(DirectCast(DescChamps, TKey), DirectCast(Champs, TValue))))
        'OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
    End Sub

#End Region

#Region "protected"

    Protected Overridable Function AddEntry(ByVal key As TKey, ByVal value As TValue) As Boolean
        Try
            _keyedEntryCollection.Add(New DictionaryEntry(key, value))
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function

    Protected Overridable Function ClearEntries() As Boolean
        ' check whether there are entries to clear
        Dim result As Boolean = (Count > 0)
        If result Then
            ' if so, clear the dictionary
            _keyedEntryCollection.Clear()
        End If
        Return result
    End Function

    Protected Function GetIndexAndEntryForKey(ByVal key As TKey, ByRef entry As DictionaryEntry) As Integer
        entry = New DictionaryEntry()
        Dim index As Integer = -1
        If _keyedEntryCollection.Contains(key) Then
            entry = _keyedEntryCollection(key)
            index = _keyedEntryCollection.IndexOf(entry)
        End If
        Return index
    End Function

    Protected Overridable Sub OnCollectionChanged(ByVal args As NotifyCollectionChangedEventArgs)
        RaiseEvent CollectionChanged(Me, args)
    End Sub

    Protected Overridable Sub OnPropertyChanged(ByVal name As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
    End Sub

    Protected Overridable Function RemoveEntry(ByVal key As TKey) As Boolean
        ' remove the entry
        Return _keyedEntryCollection.Remove(key)
    End Function

    Protected Overridable Function SetEntry(ByVal key As TKey, ByVal value As TValue) As Boolean
        Dim keyExists As Boolean = _keyedEntryCollection.Contains(key)

        ' if identical key/value pair already exists, nothing to do
        If keyExists AndAlso value.Equals(DirectCast(_keyedEntryCollection(key).Value, TValue)) Then
            Return False
        End If

        ' otherwise, remove the existing entry
        If keyExists Then
            _keyedEntryCollection.Remove(key)
        End If

        ' add the new entry
        _keyedEntryCollection.Add(New DictionaryEntry(key, value))

        Return True
    End Function

#End Region

#Region "private"

    Private Sub DoAddEntry(ByVal key As TKey, ByVal value As TValue)
        If AddEntry(key, value) Then
            System.Math.Max(System.Threading.Interlocked.Increment(_version), _version - 1)

            Dim entry As DictionaryEntry
            Dim index As Integer = GetIndexAndEntryForKey(key, entry)
            FireEntryAddedNotifications(entry, index)
        End If
    End Sub

    Private Sub DoClearEntries()
        If ClearEntries() Then
            System.Math.Max(System.Threading.Interlocked.Increment(_version), _version - 1)
            FireResetNotifications()
        End If
    End Sub

    Private Function DoRemoveEntry(ByVal key As TKey) As Boolean
        Dim entry As DictionaryEntry
        Dim index As Integer = GetIndexAndEntryForKey(key, entry)

        Dim result As Boolean = RemoveEntry(key)
        If result Then
            System.Math.Max(System.Threading.Interlocked.Increment(_version), _version - 1)
            If index > -1 Then
                FireEntryRemovedNotifications(entry, index)
            End If
        End If

        Return result
    End Function

    Private Sub DoSetEntry(ByVal key As TKey, ByVal value As TValue)
        Dim entry As DictionaryEntry
        Dim index As Integer = GetIndexAndEntryForKey(key, entry)

        If SetEntry(key, value) Then
            System.Math.Max(System.Threading.Interlocked.Increment(_version), _version - 1)

            ' if prior entry existed for this key, fire the removed notifications
            If index > -1 Then
                FireEntryRemovedNotifications(entry, index)

                ' force the property change notifications to fire for the modified entry
                System.Math.Max(System.Threading.Interlocked.Decrement(_countCache), _countCache + 1)
            End If

            ' then fire the added notifications
            index = GetIndexAndEntryForKey(key, entry)
            FireEntryAddedNotifications(entry, index)
        End If
    End Sub

    Private Sub FireEntryAddedNotifications(ByVal entry As DictionaryEntry, ByVal index As Integer)
        ' fire the relevant PropertyChanged notifications
        FirePropertyChangedNotifications()

        ' fire CollectionChanged notification
        If index > -1 Then
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, New KeyValuePair(Of TKey, TValue)(DirectCast(entry.Key, TKey), DirectCast(entry.Value, TValue)), index))
        Else
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
        End If
    End Sub

    Private Sub FireEntryRemovedNotifications(ByVal entry As DictionaryEntry, ByVal index As Integer)
        ' fire the relevant PropertyChanged notifications
        FirePropertyChangedNotifications()

        ' fire CollectionChanged notification
        If index > -1 Then
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, New KeyValuePair(Of TKey, TValue)(DirectCast(entry.Key, TKey), DirectCast(entry.Value, TValue)), index))
        Else
            OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
        End If
    End Sub

    Private Sub FirePropertyChangedNotifications()
        If Count <> _countCache Then
            _countCache = Count
            OnPropertyChanged("Count")
            OnPropertyChanged("Item[]")
            OnPropertyChanged("Keys")
            OnPropertyChanged("Values")
        End If
    End Sub

    Private Sub FireResetNotifications()
        ' fire the relevant PropertyChanged notifications
        FirePropertyChangedNotifications()

        ' fire CollectionChanged notification
        OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
    End Sub

#End Region

#End Region

#Region "interfaces"

#Region "IDictionary<TKey, TValue>"
    Sub ITDictionaryAdd(ByVal key As TKey, ByVal value As TValue) Implements IDictionary(Of TKey, TValue).Add
        DoAddEntry(key, value)
    End Sub
    Function ITDictionaryRemove(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).Remove
        Return DoRemoveEntry(key)
    End Function
    Function ITDictionaryContainsKey(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).ContainsKey
        Return _keyedEntryCollection.Contains(key)
    End Function
    Function ITDictionaryTryGetValue(ByVal key As TKey, ByRef value As TValue) As Boolean Implements IDictionary(Of TKey, TValue).TryGetValue
        Return TryGetValue(key, value)
    End Function
    ReadOnly Property ITDictionaryKeys() As ICollection(Of TKey) Implements IDictionary(Of TKey, TValue).Keys
        Get
            Return Keys
        End Get
    End Property
    ReadOnly Property ITDictionaryValues() As ICollection(Of TValue) Implements IDictionary(Of TKey, TValue).Values
        Get
            Return Values
        End Get
    End Property
    Property ITDictionaryItem(ByVal key As TKey) As TValue Implements IDictionary(Of TKey, TValue).Item
        Get
            Return DirectCast(_keyedEntryCollection(key).Value, TValue)
        End Get
        Set(ByVal value As TValue)
            DoSetEntry(key, value)
        End Set
    End Property
#End Region

#Region "IDictionary"

    Sub IDictionaryAdd(ByVal key As Object, ByVal value As Object) Implements IDictionary.Add
        DoAddEntry(DirectCast(key, TKey), DirectCast(value, TValue))
    End Sub
    Sub IDictionaryClear() Implements IDictionary.Clear
        DoClearEntries()
    End Sub
    Function IDictionaryContains(ByVal key As Object) As Boolean Implements IDictionary.Contains
        Return _keyedEntryCollection.Contains(DirectCast(key, TKey))
    End Function
    Function IDictionaryGetEnumerator() As IDictionaryEnumerator Implements IDictionary.GetEnumerator
        Return New Enumerator(Of TKey, TValue)(Me, True)
    End Function
    ReadOnly Property IDictionaryIsFixedSize() As Boolean Implements IDictionary.IsFixedSize
        Get
            Return False
        End Get
    End Property
    ReadOnly Property IDictionaryIsReadOnly() As Boolean Implements IDictionary.IsReadOnly
        Get
            Return False
        End Get
    End Property
    Property IDictionaryItem(ByVal key As Object) As Object Implements IDictionary.Item
        Get
            Return _keyedEntryCollection(DirectCast(key, TKey)).Value
        End Get
        Set(ByVal value As Object)
            DoSetEntry(DirectCast(key, TKey), DirectCast(value, TValue))
        End Set
    End Property
    ReadOnly Property IDictionaryKeys() As ICollection Implements IDictionary.Keys
        Get
            Return Keys
        End Get
    End Property
    Sub IDictionaryRemove(ByVal key As Object) Implements IDictionary.Remove
        DoRemoveEntry(DirectCast(key, TKey))
    End Sub
    ReadOnly Property IDictionaryValues() As ICollection Implements IDictionary.Values
        Get
            Return Values
        End Get
    End Property

#End Region

#Region "ICollection<KeyValuePair<TKey, TValue>>"

    Sub ITCollectionAdd(ByVal kvp As KeyValuePair(Of TKey, TValue)) Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Add
        DoAddEntry(kvp.Key, kvp.Value)
    End Sub
    Sub ITCollectionClear() Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Clear
        DoClearEntries()
    End Sub
    Function ITCollectionContains(ByVal kvp As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Contains
        Return _keyedEntryCollection.Contains(kvp.Key)
    End Function
    Sub ITCollectionCopyTo(ByVal array As KeyValuePair(Of TKey, TValue)(), ByVal index As Integer) Implements ICollection(Of KeyValuePair(Of TKey, TValue)).CopyTo
        If array Is Nothing Then
            Throw New ArgumentNullException("CopyTo() failed:  array parameter was null")
        End If
        If (index < 0) OrElse (index > array.Length) Then
            Throw New ArgumentOutOfRangeException("CopyTo() failed:  index parameter was outside the bounds of the supplied array")
        End If
        If (array.Length - index) < _keyedEntryCollection.Count Then
            Throw New ArgumentException("CopyTo() failed:  supplied array was too small")
        End If

        For Each entry As DictionaryEntry In _keyedEntryCollection
            array(System.Math.Max(System.Threading.Interlocked.Increment(index), index - 1)) = New KeyValuePair(Of TKey, TValue)(DirectCast(entry.Key, TKey), DirectCast(entry.Value, TValue))
        Next
    End Sub
    ReadOnly Property ITCollectionCount() As Integer Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Count
        Get
            Return _keyedEntryCollection.Count
        End Get
    End Property
    ReadOnly Property ITCollectionIsReadOnly() As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).IsReadOnly
        Get
            Return False
        End Get
    End Property
    Function ITCollectionRemove(ByVal kvp As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Remove
        Return DoRemoveEntry(kvp.Key)
    End Function

#End Region

#Region "ICollection"
    Sub ICollectionCopyTo(ByVal array As Array, ByVal index As Integer) Implements ICollection.CopyTo
        DirectCast(_keyedEntryCollection, ICollection).CopyTo(array, index)
    End Sub
    ReadOnly Property ICollectionCount() As Integer Implements ICollection.Count
        Get
            Return _keyedEntryCollection.Count
        End Get
    End Property
    ReadOnly Property ICollectionIsSynchronized() As Boolean Implements ICollection.IsSynchronized
        Get
            Return (DirectCast(_keyedEntryCollection, ICollection)).IsSynchronized
        End Get
    End Property
    ReadOnly Property ICollectionSyncRoot() As Object Implements ICollection.SyncRoot
        Get
            Return (DirectCast(_keyedEntryCollection, ICollection)).SyncRoot
        End Get
    End Property
#End Region

#Region "IEnumerable<KeyValuePair<TKey, TValue>>"
    Function ITEnumerableGetEnumerator() As IEnumerator(Of KeyValuePair(Of TKey, TValue)) Implements IEnumerable(Of KeyValuePair(Of TKey, TValue)).GetEnumerator
        Return New Enumerator(Of TKey, TValue)(Me, False)
    End Function
#End Region

#Region "IEnumerable"
    Function IEnumerableGetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return GetEnumerator()
    End Function
#End Region

#Region "ISerializable"
    Public Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
        If info Is Nothing Then
            Throw New ArgumentNullException("info")
        End If

        Dim entries As New Collection(Of DictionaryEntry)()
        For Each entry As DictionaryEntry In _keyedEntryCollection
            entries.Add(entry)
        Next
        info.AddValue("entries", entries)
    End Sub
#End Region

#Region "IDeserializationCallback"
    Public Overridable Sub OnDeserialization(ByVal sender As Object) Implements IDeserializationCallback.OnDeserialization
        If Not _siInfo Is Nothing Then
            Dim entries As Collection(Of DictionaryEntry) = DirectCast(_siInfo.GetValue("entries", GetType(Collection(Of DictionaryEntry))), Collection(Of DictionaryEntry))
            For Each entry As DictionaryEntry In entries
                AddEntry(DirectCast(entry.Key, TKey), DirectCast(entry.Value, TValue))
            Next
        End If
    End Sub
#End Region

#Region "INotifyCollectionChanged"
    Event CollectionChanged As NotifyCollectionChangedEventHandler Implements INotifyCollectionChanged.CollectionChanged
#End Region

#Region "INotifyPropertyChanged"
    Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
#End Region

#End Region

#Region "protected classes"

#Region "KeyedDictionaryEntryCollection<TKey>"

    Protected Class KeyedDictionaryEntryCollection(Of TKey2)
        Inherits KeyedCollection(Of TKey2, DictionaryEntry)
#Region "constructors"

#Region "public"

        Public Sub New()
            MyBase.New()
        End Sub

        Public Sub New(ByVal comparer As IEqualityComparer(Of TKey2))
            MyBase.New(comparer)
        End Sub

#End Region

#End Region

#Region "methods"

#Region "protected"

        Protected Overloads Overrides Function GetKeyForItem(ByVal entry As DictionaryEntry) As TKey2
            Return DirectCast(entry.Key, TKey2)
        End Function

#End Region

#End Region
    End Class

#End Region

#End Region

#Region "public structures"

#Region "Enumerator"
    Public Structure Enumerator(Of TKey1, TValue1)
        Implements IEnumerator(Of KeyValuePair(Of TKey1, TValue1))
        Implements IDisposable
        Implements IDictionaryEnumerator
        Implements IEnumerator
#Region "constructors"

        Friend Sub New(ByVal dictionary As ObservableDictionary(Of TKey1, TValue1), ByVal isDictionaryEntryEnumerator As Boolean)
            _dictionary = dictionary
            _version = dictionary._version
            _index = -1
            _isDictionaryEntryEnumerator = isDictionaryEntryEnumerator
            _current = New KeyValuePair(Of TKey1, TValue1)()
        End Sub

#End Region

#Region "properties"

#Region "public"

        Public ReadOnly Property IEnumeratorCurrent() As KeyValuePair(Of TKey1, TValue1) Implements IEnumerator(Of System.Collections.Generic.KeyValuePair(Of TKey1, TValue1)).Current
            Get
                ValidateCurrent()
                Return _current
            End Get
        End Property

#End Region

#End Region

#Region "methods"

#Region "public"

        Public Sub Dispose() Implements IDisposable.Dispose
        End Sub

        Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
            ValidateVersion()
            System.Math.Max(System.Threading.Interlocked.Increment(_index), _index - 1)
            If _index < _dictionary._keyedEntryCollection.Count Then
                _current = New KeyValuePair(Of TKey1, TValue1)(DirectCast(_dictionary._keyedEntryCollection(_index).Key, TKey1), DirectCast(_dictionary._keyedEntryCollection(_index).Value, TValue1))
                Return True
            End If
            _index = -2
            _current = New KeyValuePair(Of TKey1, TValue1)()
            Return False
        End Function

#End Region

#Region "private"

        Private Sub ValidateCurrent()
            If _index = -1 Then
                Throw New InvalidOperationException("The enumerator has not been started.")
            ElseIf _index = -2 Then
                Throw New InvalidOperationException("The enumerator has reached the end of the collection.")
            End If
        End Sub

        Private Sub ValidateVersion()
            If _version <> _dictionary._version Then
                Throw New InvalidOperationException("The enumerator is not valid because the dictionary changed.")
            End If
        End Sub

#End Region

#End Region

#Region "IEnumerator implementation"

        ReadOnly Property Current() As Object Implements IEnumerator.Current
            Get
                ValidateCurrent()
                If _isDictionaryEntryEnumerator Then
                    Return New DictionaryEntry(_current.Key, _current.Value)
                End If
                Return New KeyValuePair(Of TKey1, TValue1)(_current.Key, _current.Value)
            End Get
        End Property

        Sub Reset() Implements IEnumerator.Reset
            ValidateVersion()
            _index = -1
            _current = New KeyValuePair(Of TKey1, TValue1)()
        End Sub

#End Region

#Region "IDictionaryEnumerator implemenation"

        ReadOnly Property Entry() As DictionaryEntry Implements IDictionaryEnumerator.Entry
            Get
                ValidateCurrent()
                Return New DictionaryEntry(_current.Key, _current.Value)
            End Get
        End Property
        ReadOnly Property Key() As Object Implements IDictionaryEnumerator.Key
            Get
                ValidateCurrent()
                Return _current.Key
            End Get
        End Property
        ReadOnly Property Value() As Object Implements IDictionaryEnumerator.Value
            Get
                ValidateCurrent()
                Return _current.Value
            End Get
        End Property

#End Region

#Region "fields"

        Private _dictionary As ObservableDictionary(Of TKey1, TValue1)
        Private _version As Integer
        Private _index As Integer
        Private _current As KeyValuePair(Of TKey1, TValue1)
        Private _isDictionaryEntryEnumerator As Boolean

#End Region
    End Structure
#End Region

#End Region

End Class